using System;
using System.Collections.Generic;
using UnityEngine;

namespace IQIGame.Onigao.Framework
{
    [DisallowMultipleComponent]
    public sealed partial class ObjectPoolManager : FrameMonoModule
    {
        [SerializeField]
        private float m_DefaultAutoReleaseInterval = 60;
        [SerializeField]
        private int m_DefaultSpecificResCapacity = 1;
        [SerializeField]
        private int m_DefaultTotalCapacity = 100;

        private Dictionary<ObjPoolTypeGroupKey, ObjectPoolBase> m_ObjectPools;

        /// <summary>
        /// 获取对象池数量。
        /// </summary>
        public int Count
        {
            get
            {
                return m_ObjectPools.Count;
            }
        }

        protected override void OnInit()
        {
            m_ObjectPools = new Dictionary<ObjPoolTypeGroupKey, ObjectPoolBase>();
            FrameworkEntry.MonoDriver.updateEvent += OnUpdate;
        }

        protected override void OnEnterGame()
        {

        }

        public void ModifyDefaultArgs(float autoReleaseInterval, int specificResCapacity, int totalCapacity)
        {
            m_DefaultAutoReleaseInterval = autoReleaseInterval;
            m_DefaultSpecificResCapacity = specificResCapacity;
            m_DefaultTotalCapacity = totalCapacity;
        }

        /// <summary>
        /// 对象池管理器轮询。
        /// </summary>
        /// <param name="elapseSeconds">逻辑流逝时间，以秒为单位。</param>
        /// <param name="realElapseSeconds">真实流逝时间，以秒为单位。</param>
        public void OnUpdate(float deltaTime, float unscaledDeltaTime)
        {
            foreach (KeyValuePair<ObjPoolTypeGroupKey, ObjectPoolBase> objectPool in m_ObjectPools)
            {
                objectPool.Value.OnUpdate(deltaTime, unscaledDeltaTime);
            }
        }

        /// <summary>
        /// 检查是否存在对象池。
        /// </summary>
        /// <typeparam name="T">对象类型。</typeparam>
        /// <param name="id">对象池名称。</param>
        /// <returns>是否存在对象池。</returns>
        public bool HasObjectPool<T>(int id) where T : IPoolObject
        {
            return HasObjectPool(ObjectPoolUtil.GetFullName<T>(id));
        }

        /// <summary>
        /// 检查是否存在对象池。
        /// </summary>
        /// <param name="objectType">对象类型。</param>
        /// <param name="id">对象池名称。</param>
        /// <returns>是否存在对象池。</returns>
        public bool HasObjectPool(Type objectType, int id)
        {
            if (objectType == null)
            {
                throw new FrameworkException("Object type is invalid.");
            }

            if (!typeof(IPoolObject).IsAssignableFrom(objectType))
            {
                throw new FrameworkException(string.Format("Object type '{0}' is invalid.", objectType.FullName));
            }

            return HasObjectPool(ObjectPoolUtil.GetFullName(objectType, id));
        }

        /// <summary>
        /// 检查是否存在对象池。
        /// </summary>
        /// <param name="fullName">对象池完整名称。</param>
        /// <returns>是否存在对象池。</returns>
        public bool HasObjectPool(ObjPoolTypeGroupKey fullName)
        {
            return m_ObjectPools.ContainsKey(fullName);
        }

        /// <summary>
        /// 检查是否存在对象池。
        /// </summary>
        /// <param name="condition">要检查的条件。</param>
        /// <returns>是否存在对象池。</returns>
        public bool HasObjectPool(Predicate<ObjectPoolBase> condition)
        {
            if (condition == null)
            {
                throw new FrameworkException("Condition is invalid.");
            }

            foreach (KeyValuePair<ObjPoolTypeGroupKey, ObjectPoolBase> objectPool in m_ObjectPools)
            {
                if (condition(objectPool.Value))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// 获取对象池。
        /// </summary>
        /// <typeparam name="T">对象类型。</typeparam>
        /// <param name="id">对象池id。</param>
        /// <returns>要获取的对象池。</returns>
        public ObjectPool<T> GetObjectPool<T>(int id) where T : class, IPoolObject, new()
        {
            return (ObjectPool<T>)GetObjectPool(ObjectPoolUtil.GetFullName<T>(id));
        }

        /// <summary>
        /// 获取对象池。
        /// </summary>
        /// <param name="objectType">对象类型。</param>
        /// <param name="id">对象池名称。</param>
        /// <returns>要获取的对象池。</returns>
        public ObjectPoolBase GetObjectPool(Type objectType, int id)
        {
            if (objectType == null)
            {
                throw new FrameworkException("Object type is invalid.");
            }

            if (!typeof(IPoolObject).IsAssignableFrom(objectType))
            {
                throw new FrameworkException(string.Format("Object type '{0}' is invalid.", objectType.FullName));
            }

            return GetObjectPool(ObjectPoolUtil.GetFullName(objectType, id));
        }

        /// <summary>
        /// 获取对象池。
        /// </summary>
        /// <param name="fullName">对象池完整名称。</param>
        /// <returns>要获取的对象池。</returns>
        public ObjectPoolBase GetObjectPool(ObjPoolTypeGroupKey fullName)
        {
            if (m_ObjectPools.TryGetValue(fullName, out var objectPool))
            {
                return objectPool;
            }

            return null;
        }

        /// <summary>
        /// 获取对象池。
        /// </summary>
        /// <param name="condition">要检查的条件。</param>
        /// <returns>要获取的对象池。</returns>
        public ObjectPoolBase GetObjectPool(Predicate<ObjectPoolBase> condition)
        {
            if (condition == null)
            {
                throw new FrameworkException("Condition is invalid.");
            }

            foreach (KeyValuePair<ObjPoolTypeGroupKey, ObjectPoolBase> objectPool in m_ObjectPools)
            {
                if (condition(objectPool.Value))
                {
                    return objectPool.Value;
                }
            }

            return null;
        }

        /// <summary>
        /// 获取对象池。
        /// </summary>
        /// <param name="condition">要检查的条件。</param>
        /// <returns>要获取的对象池。</returns>
        public ObjectPoolBase[] GetObjectPools(Predicate<ObjectPoolBase> condition)
        {
            if (condition == null)
            {
                throw new FrameworkException("Condition is invalid.");
            }

            List<ObjectPoolBase> results = new List<ObjectPoolBase>();
            foreach (var objectPool in m_ObjectPools)
            {
                if (condition(objectPool.Value))
                {
                    results.Add(objectPool.Value);
                }
            }

            return results.ToArray();
        }

        /// <summary>
        /// 获取对象池。
        /// </summary>
        /// <param name="condition">要检查的条件。</param>
        /// <param name="results">要获取的对象池。</param>
        public void GetObjectPools(Predicate<ObjectPoolBase> condition, List<ObjectPoolBase> results)
        {
            if (condition == null)
            {
                throw new FrameworkException("Condition is invalid.");
            }

            if (results == null)
            {
                throw new FrameworkException("Results is invalid.");
            }

            results.Clear();
            foreach (var objectPool in m_ObjectPools)
            {
                if (condition(objectPool.Value))
                {
                    results.Add(objectPool.Value);
                }
            }
        }

        /// <summary>
        /// 获取所有对象池。
        /// </summary>
        /// <param name="sort">是否根据对象池的优先级排序。</param>
        /// <returns>所有对象池。</returns>
        public ObjectPoolBase[] GetAllObjectPools()
        {
            int index = 0;
            ObjectPoolBase[] results = new ObjectPoolBase[m_ObjectPools.Count];
            foreach (var objectPool in m_ObjectPools)
            {
                results[index++] = objectPool.Value;
            }

            return results;
        }

        /// <summary>
        /// 获取所有对象池。
        /// </summary>
        /// <param name="sort">是否根据对象池的优先级排序。</param>
        /// <param name="results">所有对象池。</param>
        public void GetAllObjectPools(List<ObjectPoolBase> results)
        {
            if (results == null)
            {
                throw new FrameworkException("Results is invalid.");
            }

            results.Clear();
            foreach (var objectPool in m_ObjectPools)
            {
                results.Add(objectPool.Value);
            }
        }

        /// <summary>
        /// 创建允许单次获取的对象池。
        /// </summary>
        /// <param name="objectType">对象类型。</param>
        /// <param name="id">对象池名称。</param>
        /// <param name="expireTime">对象池对象过期秒数。</param>
        /// <returns>要创建的允许单次获取的对象池。</returns>
        public ObjectPoolBase CreateObjectPool(Type objectType, int id, float expireTime)
        {
            return CreateObjectPool(objectType, id, m_DefaultAutoReleaseInterval, m_DefaultSpecificResCapacity, m_DefaultTotalCapacity, expireTime);
        }

        /// <summary>
        /// 销毁对象池。
        /// </summary>
        /// <typeparam name="T">对象类型。</typeparam>
        /// <param name="id">要销毁的对象池名称。</param>
        /// <returns>是否销毁对象池成功。</returns>
        public bool DestroyObjectPool<T>(int id) where T : IPoolObject
        {
            return InternalDestroyObjectPool(ObjectPoolUtil.GetFullName<T>(id));
        }

        /// <summary>
        /// 销毁对象池。
        /// </summary>
        /// <param name="objectType">对象类型。</param>
        /// <param name="id">要销毁的对象池名称。</param>
        /// <returns>是否销毁对象池成功。</returns>
        public bool DestroyObjectPool(Type objectType, int id)
        {
            if (objectType == null)
            {
                throw new FrameworkException("Object type is invalid.");
            }

            if (!typeof(IPoolObject).IsAssignableFrom(objectType))
            {
                throw new FrameworkException(string.Format("Object type '{0}' is invalid.", objectType.FullName));
            }

            return InternalDestroyObjectPool(ObjectPoolUtil.GetFullName(objectType, id));
        }

        /// <summary>
        /// 销毁对象池。
        /// </summary>
        /// <param name="objectType">对象类型。</param>
        /// <param name="id">要销毁的对象池名称。</param>
        /// <returns>是否销毁对象池成功。</returns>
        public bool DestroyObjectPool(ObjectPoolBase pool)
        {
            if (pool == null)
            {
                throw new FrameworkException("pool is null.");
            }

            if (!typeof(IPoolObject).IsAssignableFrom(pool.ObjectType))
            {
                throw new FrameworkException(string.Format("Object type '{0}' is invalid.", pool.ObjectType.FullName));
            }

            return InternalDestroyObjectPool(ObjectPoolUtil.GetFullName(pool.ObjectType, pool.Id));
        }

        /// <summary>
        /// 释放对象池中的可释放对象。
        /// </summary>
        public void Release()
        {
            ObjectPoolBase[] objectPools = GetAllObjectPools();
            foreach (ObjectPoolBase objectPool in objectPools)
            {
                objectPool.Release();
            }
        }

        /// <summary>
        /// 释放对象池中的所有对象。
        /// </summary>
        public void ForceReleaseAll()
        {
            ObjectPoolBase[] objectPools = GetAllObjectPools();
            foreach (ObjectPoolBase objectPool in objectPools)
            {
                objectPool.ForceReleaseAll();
            }
        }

        public ObjectPool<T> CreateObjectPool<T>(int id, float autoReleaseInterval, int specificResCapacity, int totalCapacity, float expireTime) where T : class, IPoolObject, new()
        {
            if (HasObjectPool<T>(id))
            {
                throw new FrameworkException(string.Format("Already exist object pool '{0}'.", ObjectPoolUtil.GetFullName<T>(id)));
            }

            ObjectPool<T> objectPool = new ObjectPool<T>(id, autoReleaseInterval, specificResCapacity, totalCapacity, expireTime);
            m_ObjectPools.Add(ObjectPoolUtil.GetFullName<T>(id), objectPool);
            return objectPool;
        }

        public ObjectPoolBase CreateObjectPool(Type objectType, int id, float autoReleaseInterval, int specificResCapacity, int totalCapacity, float expireTime)
        {
            if (objectType == null)
            {
                throw new FrameworkException("Object type is invalid.");
            }

            if (!typeof(IPoolObject).IsAssignableFrom(objectType))
            {
                throw new FrameworkException(string.Format("Object type '{0}' is invalid.", objectType.FullName));
            }

            if (HasObjectPool(objectType, id))
            {
                throw new FrameworkException(string.Format("Already exist object pool '{0}'.", ObjectPoolUtil.GetFullName(objectType, id)));
            }

            Type objectPoolType = typeof(ObjectPool<>).MakeGenericType(objectType);
            ObjectPoolBase objectPool = (ObjectPoolBase)Activator.CreateInstance(objectPoolType, id, autoReleaseInterval, specificResCapacity, totalCapacity, expireTime);
            m_ObjectPools.Add(ObjectPoolUtil.GetFullName(objectType, id), objectPool);
            return objectPool;
        }

        private bool InternalDestroyObjectPool(ObjPoolTypeGroupKey fullName)
        {
            if (m_ObjectPools.TryGetValue(fullName, out var objectPool))
            {
                objectPool.Dispose();
                return m_ObjectPools.Remove(fullName);
            }

            return false;
        }

        protected override void OnShutdown()
        {
            FrameworkEntry.MonoDriver.updateEvent -= OnUpdate;

            foreach (var objectPool in m_ObjectPools)
            {
                objectPool.Value.Dispose();
            }

            m_ObjectPools.Clear();
        }
    }
}
